package evolution.operation.reproduction;

import evolution.individual.Individual;
import evolution.individual.IndividualsFactory;
import evolution.random.RandomGenerator;

public class BLXaReproduction implements ReproductionStrategy {

	private RandomGenerator randomGenerator;
	private IndividualsFactory individualsFactory;
	private double probability = 0.4;
	
	@Override
	public Individual[] breed(Individual[] parents) {
		int parentsCount = parents.length;
		int dimensionsCount = parents[0].getChromosome().length;

		Individual[] offspring = new Individual[parentsCount];
		parents = mixUpParentsOrder(parents);
		
		for(int i = 0; i < parentsCount-1; i = i + 2) {
			Individual parent1 = parents[i];
			Individual parent2 = parents[i + 1];
			
			if (randomGenerator.nextDouble() < probability) {
				double[] chromosome1 = parent1.getChromosome();
				double[] chromosome2 = parent2.getChromosome();
				double[] descendant1 = new double[dimensionsCount];
				double[] descendant2 = new double[dimensionsCount];
				crossing(chromosome1, chromosome2, descendant1, descendant2);
				
				Individual offspring1 = individualsFactory.create();
				offspring1.setChromosome(descendant1);
				offspring[i] = offspring1;
				
				Individual offspring2 = individualsFactory.create();
				offspring2.setChromosome(descendant2);
				offspring[i+1] = offspring2;
			}
			else {
				offspring[i] = parent1.clone();
				offspring[i+1] = parent2.clone();
			}
			
			if(parents.length%2 == 1) {
				offspring[parents.length-1] = parents[parents.length-1].clone();
			}
		}
		
		return offspring;
	}

	private Individual[] mixUpParentsOrder(Individual[] parents) {
		int parentsCount = parents.length;
		Individual a;
		Individual b;
		Individual temp;
		int powerOfMixing = (int) parents.length / 2;
		for (int i = 0; i < powerOfMixing; i++) {
			a = parents[randomGenerator.nextInt(parentsCount)];
			b = parents[randomGenerator.nextInt(parentsCount)];
			temp = a;
			a = b;
			b = temp;
		}
		return parents;
	}
	
	private void crossing(double[] chromosome1, double[] chromosome2,
			double[] descendant1, double[] descendant2) {
		double a = 0.2;
		double max;
		double min;
		double I;
		
		double maxRange;
		double minRange;
		
		for(int i=0; i<chromosome1.length; i++) {
			max = max(chromosome1[i], chromosome2[i]);
			min = min(chromosome1[i], chromosome2[i]);
			I = max - min;
			
			maxRange = max + (I*a);
			minRange = min - (I*a);
			
			
			descendant1[i] = randomGenerator.nextDouble()*(maxRange-minRange)+minRange;
			descendant2[i] = randomGenerator.nextDouble()*(maxRange-minRange)+minRange;
		}
	}
	
	private double max(double d1, double d2) {
		if(d1 >= d2) {
			return d1;
		}
		else {
			return d2;
		}
	}
	
	private double min(double d1, double d2) {
		if(d1 >= d2) {
			return d2;
		}
		else {
			return d1;
		}
	}

	private double difference(double d1, double d2) {
		if(d2 < 0) {
			return d1 - ((-1)*d2);
		}
		else {
			return d1 - d2;
		}
	}
	
	@Override
	public double getProbability() {
		return probability;
	}

	@Override
	public void setProbability(double probability) {
		this.probability = probability;
	}

	@Override
	public RandomGenerator getRandomGenerator() {
		return randomGenerator;
	}

	@Override
	public void setRandomGenerator(RandomGenerator randomGenerator) {
		this.randomGenerator = randomGenerator;
	}
	
	public IndividualsFactory getIndividualsFactory() {
		return individualsFactory;
	}

	public void setIndividualsFactory(IndividualsFactory individualsFactory) {
		this.individualsFactory = individualsFactory;
	}

}
